const { AdView, User, sequelize } = require('../models')
const logger = require('../utils/logger')
const redisClient = require('../config/redis')
const { Op } = require('sequelize')
const crypto = require('crypto')

/**
 * 防刷服务
 */
class AntiCheatService {
  /**
   * 验证广告观看请求的合法性
   * @param {number} userId - 用户ID
   * @param {Object} adInfo - 广告信息
   * @param {string} ipAddress - IP地址
   * @param {string} userAgent - 用户代理
   * @returns {Object} 验证结果
   */
  static async validateAdView(userId, adInfo, ipAddress, userAgent) {
    const checks = [
      this.checkTimestamp(adInfo.timestamp),
      this.checkUserDailyLimit(userId),
      this.checkIPDailyLimit(ipAddress),
      this.checkViewInterval(userId, adInfo.adType),
      this.checkDeviceFingerprint(userId, ipAddress, userAgent),
      this.checkSuspiciousPattern(userId, ipAddress),
      this.checkGeoLocation(ipAddress),
      this.checkUserBehavior(userId)
    ]
    
    const results = await Promise.all(checks)
    
    // 检查是否有任何验证失败
    for (const result of results) {
      if (!result.valid) {
        logger.warn(`广告观看验证失败: ${result.reason}`, {
          userId,
          ipAddress,
          userAgent: userAgent?.substring(0, 100)
        })
        return result
      }
    }
    
    return { valid: true }
  }
  
  /**
   * 检查时间戳是否合理
   * @param {number} timestamp - 时间戳
   * @returns {Object} 检查结果
   */
  static async checkTimestamp(timestamp) {
    const now = Date.now()
    const timeDiff = Math.abs(now - timestamp)
    
    // 允许5分钟的时间误差
    if (timeDiff > 300000) {
      return {
        valid: false,
        reason: '请求时间异常',
        riskLevel: 'high'
      }
    }
    
    return { valid: true }
  }
  
  /**
   * 检查用户每日观看限制
   * @param {number} userId - 用户ID
   * @returns {Object} 检查结果
   */
  static async checkUserDailyLimit(userId) {
    const today = new Date()
    today.setHours(0, 0, 0, 0)
    const tomorrow = new Date(today)
    tomorrow.setDate(tomorrow.getDate() + 1)
    
    const todayCount = await AdView.count({
      where: {
        user_id: userId,
        created_at: {
          [Op.between]: [today, tomorrow]
        }
      }
    })
    
    const dailyLimit = 50
    if (todayCount >= dailyLimit) {
      return {
        valid: false,
        reason: '今日观看次数已达上限',
        riskLevel: 'medium'
      }
    }
    
    return { valid: true }
  }
  
  /**
   * 检查IP每日观看限制
   * @param {string} ipAddress - IP地址
   * @returns {Object} 检查结果
   */
  static async checkIPDailyLimit(ipAddress) {
    const today = new Date()
    today.setHours(0, 0, 0, 0)
    const tomorrow = new Date(today)
    tomorrow.setDate(tomorrow.getDate() + 1)
    
    const ipCount = await AdView.count({
      where: {
        ip_address: ipAddress,
        created_at: {
          [Op.between]: [today, tomorrow]
        }
      }
    })
    
    const ipDailyLimit = 100
    if (ipCount >= ipDailyLimit) {
      return {
        valid: false,
        reason: 'IP观看次数超限',
        riskLevel: 'high'
      }
    }
    
    return { valid: true }
  }
  
  /**
   * 检查观看时间间隔
   * @param {number} userId - 用户ID
   * @param {string} adType - 广告类型
   * @returns {Object} 检查结果
   */
  static async checkViewInterval(userId, adType) {
    const minInterval = 30000 // 30秒
    const recentTime = new Date(Date.now() - minInterval)
    
    const recentViews = await AdView.count({
      where: {
        user_id: userId,
        ad_type: adType,
        created_at: {
          [Op.gte]: recentTime
        }
      }
    })
    
    if (recentViews > 0) {
      return {
        valid: false,
        reason: '观看过于频繁，请稍后再试',
        riskLevel: 'medium'
      }
    }
    
    return { valid: true }
  }
  
  /**
   * 检查设备指纹
   * @param {number} userId - 用户ID
   * @param {string} ipAddress - IP地址
   * @param {string} userAgent - 用户代理
   * @returns {Object} 检查结果
   */
  static async checkDeviceFingerprint(userId, ipAddress, userAgent) {
    const fingerprint = this.generateDeviceFingerprint(ipAddress, userAgent)
    
    // 检查同一设备指纹的观看频率
    const recentTime = new Date(Date.now() - 60000) // 1分钟内
    
    const fingerprintViews = await AdView.count({
      where: {
        ip_address: ipAddress,
        user_agent: userAgent,
        created_at: {
          [Op.gte]: recentTime
        }
      }
    })
    
    if (fingerprintViews > 3) {
      return {
        valid: false,
        reason: '设备异常，请稍后再试',
        riskLevel: 'high'
      }
    }
    
    // 检查设备指纹是否在黑名单中
    const isBlacklisted = await this.isDeviceFingerprintBlacklisted(fingerprint)
    if (isBlacklisted) {
      return {
        valid: false,
        reason: '设备已被限制',
        riskLevel: 'high'
      }
    }
    
    return { valid: true }
  }
  
  /**
   * 检查可疑模式
   * @param {number} userId - 用户ID
   * @param {string} ipAddress - IP地址
   * @returns {Object} 检查结果
   */
  static async checkSuspiciousPattern(userId, ipAddress) {
    // 检查是否存在批量注册的可疑IP
    const suspiciousIP = await this.checkSuspiciousIP(ipAddress)
    if (suspiciousIP.suspicious) {
      return {
        valid: false,
        reason: suspiciousIP.reason,
        riskLevel: 'high'
      }
    }
    
    // 检查用户行为模式
    const behaviorPattern = await this.analyzeUserBehaviorPattern(userId)
    if (behaviorPattern.suspicious) {
      return {
        valid: false,
        reason: behaviorPattern.reason,
        riskLevel: 'medium'
      }
    }
    
    return { valid: true }
  }
  
  /**
   * 检查地理位置
   * @param {string} ipAddress - IP地址
   * @returns {Object} 检查结果
   */
  static async checkGeoLocation(ipAddress) {
    try {
      // 这里可以集成IP地理位置服务
      // 检查是否来自高风险地区或使用代理
      const geoInfo = await this.getIPGeoLocation(ipAddress)
      
      if (geoInfo.isProxy || geoInfo.isVPN) {
        return {
          valid: false,
          reason: '检测到代理访问',
          riskLevel: 'high'
        }
      }
      
      if (geoInfo.riskLevel === 'high') {
        return {
          valid: false,
          reason: '地理位置异常',
          riskLevel: 'medium'
        }
      }
      
    } catch (error) {
      logger.warn('地理位置检查失败:', error)
      // 地理位置检查失败不阻止用户观看
    }
    
    return { valid: true }
  }
  
  /**
   * 检查用户行为
   * @param {number} userId - 用户ID
   * @returns {Object} 检查结果
   */
  static async checkUserBehavior(userId) {
    try {
      // 获取用户最近的行为数据
      const user = await User.findByPk(userId)
      if (!user) {
        return {
          valid: false,
          reason: '用户不存在',
          riskLevel: 'high'
        }
      }
      
      // 检查用户注册时间（新注册用户限制更严格）
      const registrationTime = new Date(user.created_at)
      const now = new Date()
      const daysSinceRegistration = (now - registrationTime) / (1000 * 60 * 60 * 24)
      
      if (daysSinceRegistration < 1) {
        // 新用户限制更严格
        const todayViews = await this.getUserTodayViews(userId)
        if (todayViews > 10) {
          return {
            valid: false,
            reason: '新用户观看次数限制',
            riskLevel: 'medium'
          }
        }
      }
      
      // 检查用户是否在黑名单中
      const isBlacklisted = await this.isUserBlacklisted(userId)
      if (isBlacklisted) {
        return {
          valid: false,
          reason: '用户已被限制',
          riskLevel: 'high'
        }
      }
      
    } catch (error) {
      logger.warn('用户行为检查失败:', error)
    }
    
    return { valid: true }
  }
  
  /**
   * 生成设备指纹
   * @param {string} ipAddress - IP地址
   * @param {string} userAgent - 用户代理
   * @returns {string} 设备指纹
   */
  static generateDeviceFingerprint(ipAddress, userAgent) {
    const data = `${ipAddress}-${userAgent}`
    return crypto.createHash('md5').update(data).digest('hex')
  }
  
  /**
   * 检查设备指纹是否在黑名单中
   * @param {string} fingerprint - 设备指纹
   * @returns {boolean} 是否在黑名单中
   */
  static async isDeviceFingerprintBlacklisted(fingerprint) {
    try {
      const key = `blacklist:device:${fingerprint}`
      const result = await redisClient.get(key)
      return result === '1'
    } catch (error) {
      logger.warn('检查设备指纹黑名单失败:', error)
      return false
    }
  }
  
  /**
   * 检查可疑IP
   * @param {string} ipAddress - IP地址
   * @returns {Object} 检查结果
   */
  static async checkSuspiciousIP(ipAddress) {
    try {
      // 检查IP是否在黑名单中
      const isBlacklisted = await this.isIPBlacklisted(ipAddress)
      if (isBlacklisted) {
        return {
          suspicious: true,
          reason: 'IP已被限制'
        }
      }
      
      // 检查IP下的用户注册数量
      const userCount = await User.count({
        where: {
          // 假设我们记录了用户注册时的IP
          // register_ip: ipAddress,
          created_at: {
            [Op.gte]: new Date(Date.now() - 24 * 60 * 60 * 1000) // 24小时内
          }
        }
      })
      
      if (userCount > 10) {
        return {
          suspicious: true,
          reason: '批量注册IP'
        }
      }
      
    } catch (error) {
      logger.warn('检查可疑IP失败:', error)
    }
    
    return { suspicious: false }
  }
  
  /**
   * 分析用户行为模式
   * @param {number} userId - 用户ID
   * @returns {Object} 分析结果
   */
  static async analyzeUserBehaviorPattern(userId) {
    try {
      // 获取用户最近的观看记录
      const recentViews = await AdView.findAll({
        where: {
          user_id: userId,
          created_at: {
            [Op.gte]: new Date(Date.now() - 24 * 60 * 60 * 1000) // 24小时内
          }
        },
        order: [['created_at', 'ASC']]
      })
      
      if (recentViews.length < 5) {
        return { suspicious: false }
      }
      
      // 检查观看时间间隔是否过于规律
      const intervals = []
      for (let i = 1; i < recentViews.length; i++) {
        const interval = new Date(recentViews[i].created_at) - new Date(recentViews[i-1].created_at)
        intervals.push(interval)
      }
      
      // 计算间隔的标准差
      const avgInterval = intervals.reduce((a, b) => a + b, 0) / intervals.length
      const variance = intervals.reduce((sum, interval) => sum + Math.pow(interval - avgInterval, 2), 0) / intervals.length
      const stdDev = Math.sqrt(variance)
      
      // 如果标准差很小，说明观看时间过于规律，可能是机器行为
      if (stdDev < 5000 && avgInterval < 60000) { // 标准差小于5秒且平均间隔小于1分钟
        return {
          suspicious: true,
          reason: '观看模式异常'
        }
      }
      
      // 检查观看完成率是否异常
      const completedViews = recentViews.filter(view => view.is_completed).length
      const completionRate = completedViews / recentViews.length
      
      // 完成率过高或过低都可疑
      if (completionRate > 0.95 || completionRate < 0.1) {
        return {
          suspicious: true,
          reason: '观看完成率异常'
        }
      }
      
    } catch (error) {
      logger.warn('分析用户行为模式失败:', error)
    }
    
    return { suspicious: false }
  }
  
  /**
   * 获取IP地理位置信息
   * @param {string} ipAddress - IP地址
   * @returns {Object} 地理位置信息
   */
  static async getIPGeoLocation(ipAddress) {
    // 这里可以集成第三方IP地理位置服务
    // 例如：MaxMind GeoIP2, IP2Location等
    
    // 模拟实现
    return {
      country: 'CN',
      region: 'Beijing',
      city: 'Beijing',
      isProxy: false,
      isVPN: false,
      riskLevel: 'low'
    }
  }
  
  /**
   * 检查IP是否在黑名单中
   * @param {string} ipAddress - IP地址
   * @returns {boolean} 是否在黑名单中
   */
  static async isIPBlacklisted(ipAddress) {
    try {
      const key = `blacklist:ip:${ipAddress}`
      const result = await redisClient.get(key)
      return result === '1'
    } catch (error) {
      logger.warn('检查IP黑名单失败:', error)
      return false
    }
  }
  
  /**
   * 检查用户是否在黑名单中
   * @param {number} userId - 用户ID
   * @returns {boolean} 是否在黑名单中
   */
  static async isUserBlacklisted(userId) {
    try {
      const key = `blacklist:user:${userId}`
      const result = await redisClient.get(key)
      return result === '1'
    } catch (error) {
      logger.warn('检查用户黑名单失败:', error)
      return false
    }
  }
  
  /**
   * 获取用户今日观看次数
   * @param {number} userId - 用户ID
   * @returns {number} 观看次数
   */
  static async getUserTodayViews(userId) {
    const today = new Date()
    today.setHours(0, 0, 0, 0)
    const tomorrow = new Date(today)
    tomorrow.setDate(tomorrow.getDate() + 1)
    
    return await AdView.count({
      where: {
        user_id: userId,
        created_at: {
          [Op.between]: [today, tomorrow]
        }
      }
    })
  }
  
  /**
   * 添加IP到黑名单
   * @param {string} ipAddress - IP地址
   * @param {number} duration - 持续时间（秒）
   * @param {string} reason - 原因
   */
  static async addIPToBlacklist(ipAddress, duration = 86400, reason = '违规行为') {
    try {
      const key = `blacklist:ip:${ipAddress}`
      await redisClient.setex(key, duration, '1')
      
      logger.info(`IP ${ipAddress} 已添加到黑名单`, {
        duration,
        reason
      })
    } catch (error) {
      logger.error('添加IP到黑名单失败:', error)
    }
  }
  
  /**
   * 添加用户到黑名单
   * @param {number} userId - 用户ID
   * @param {number} duration - 持续时间（秒）
   * @param {string} reason - 原因
   */
  static async addUserToBlacklist(userId, duration = 86400, reason = '违规行为') {
    try {
      const key = `blacklist:user:${userId}`
      await redisClient.setex(key, duration, '1')
      
      logger.info(`用户 ${userId} 已添加到黑名单`, {
        duration,
        reason
      })
    } catch (error) {
      logger.error('添加用户到黑名单失败:', error)
    }
  }
  
  /**
   * 添加设备指纹到黑名单
   * @param {string} fingerprint - 设备指纹
   * @param {number} duration - 持续时间（秒）
   * @param {string} reason - 原因
   */
  static async addDeviceFingerprintToBlacklist(fingerprint, duration = 86400, reason = '违规行为') {
    try {
      const key = `blacklist:device:${fingerprint}`
      await redisClient.setex(key, duration, '1')
      
      logger.info(`设备指纹 ${fingerprint} 已添加到黑名单`, {
        duration,
        reason
      })
    } catch (error) {
      logger.error('添加设备指纹到黑名单失败:', error)
    }
  }
  
  /**
   * 记录可疑行为
   * @param {Object} suspiciousActivity - 可疑活动信息
   */
  static async recordSuspiciousActivity(suspiciousActivity) {
    try {
      const {
        userId,
        ipAddress,
        userAgent,
        activityType,
        riskLevel,
        reason,
        metadata
      } = suspiciousActivity
      
      // 记录到数据库或日志系统
      logger.warn('检测到可疑活动', {
        userId,
        ipAddress,
        userAgent: userAgent?.substring(0, 100),
        activityType,
        riskLevel,
        reason,
        metadata,
        timestamp: new Date().toISOString()
      })
      
      // 根据风险级别采取相应措施
      if (riskLevel === 'high') {
        // 高风险：立即加入黑名单
        if (userId) {
          await this.addUserToBlacklist(userId, 86400, reason)
        }
        if (ipAddress) {
          await this.addIPToBlacklist(ipAddress, 86400, reason)
        }
      } else if (riskLevel === 'medium') {
        // 中风险：增加监控
        await this.increaseMonitoring(userId, ipAddress)
      }
      
    } catch (error) {
      logger.error('记录可疑行为失败:', error)
    }
  }
  
  /**
   * 增加监控
   * @param {number} userId - 用户ID
   * @param {string} ipAddress - IP地址
   */
  static async increaseMonitoring(userId, ipAddress) {
    try {
      if (userId) {
        const key = `monitor:user:${userId}`
        await redisClient.incr(key)
        await redisClient.expire(key, 86400) // 24小时过期
      }
      
      if (ipAddress) {
        const key = `monitor:ip:${ipAddress}`
        await redisClient.incr(key)
        await redisClient.expire(key, 86400) // 24小时过期
      }
    } catch (error) {
      logger.error('增加监控失败:', error)
    }
  }
}

module.exports = AntiCheatService